package cc.shacocloud.mirage.utils;

import cc.shacocloud.mirage.utils.charSequence.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 用于处理包含占位符值的字符串的实用程序类。占位符采用 {@code {name}} 的形式。使用 {@code PropertyPlaceholderHelper}，可以将这些占位符替换为用户提供的值
 * <p>
 * 参考spring
 *
 * @author 思追(shaco)
 */
@Slf4j
public class PropertyPlaceholderHelper {
    
    private static final Map<String, String> wellKnownSimplePrefixes = new HashMap<>(4);
    
    static {
        wellKnownSimplePrefixes.put("}", "{");
        wellKnownSimplePrefixes.put("]", "[");
        wellKnownSimplePrefixes.put(")", "(");
    }
    
    
    private final String placeholderPrefix;
    
    private final String placeholderSuffix;
    
    private final String simplePrefix;
    
    @Nullable
    private final String valueSeparator;
    
    private final boolean ignoreUnresolvablePlaceholders;
    
    /**
     * @param placeholderPrefix 表示占位符开头的前缀
     * @param placeholderSuffix 表示占位符结尾的后缀
     */
    public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix) {
        this(placeholderPrefix, placeholderSuffix, null, true);
    }
    
    /**
     * @param placeholderPrefix              表示占位符开头的前缀
     * @param placeholderSuffix              表示占位符结尾的后缀
     * @param valueSeparator                 占位符变量和关联的默认值之间的分隔字符
     * @param ignoreUnresolvablePlaceholders 指示是应忽略不可解析的占位符 （{@code true}） 还是导致异常 （{@code false}）
     */
    public PropertyPlaceholderHelper(@NotNull String placeholderPrefix,
                                     @NotNull String placeholderSuffix,
                                     @Nullable String valueSeparator,
                                     boolean ignoreUnresolvablePlaceholders) {
        this.placeholderPrefix = placeholderPrefix;
        this.placeholderSuffix = placeholderSuffix;
        String simplePrefixForSuffix = wellKnownSimplePrefixes.get(this.placeholderSuffix);
        if (simplePrefixForSuffix != null && this.placeholderPrefix.endsWith(simplePrefixForSuffix)) {
            this.simplePrefix = simplePrefixForSuffix;
        } else {
            this.simplePrefix = this.placeholderPrefix;
        }
        this.valueSeparator = valueSeparator;
        this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
    }
    
    /**
     * 占位符替换为从提供的 {@link PlaceholderResolver}返回的值
     *
     * @param value               包含要替换的占位符的值
     * @param placeholderResolver 用于替换的 {@code PlaceholderResolver}
     * @return 提供的值，占位符以内联方式替换
     */
    public String replacePlaceholders(@NotNull String value, @NotNull PlaceholderResolver placeholderResolver) {
        return parseStringValue(value, placeholderResolver, null);
    }
    
    protected String parseStringValue(@NotNull String value,
                                      @NotNull PlaceholderResolver placeholderResolver,
                                      @Nullable Set<String> visitedPlaceholders) {
        
        int startIndex = value.indexOf(this.placeholderPrefix);
        if (startIndex == -1) {
            return value;
        }
        
        StringBuilder result = new StringBuilder(value);
        while (startIndex != -1) {
            int endIndex = findPlaceholderEndIndex(result, startIndex);
            if (endIndex != -1) {
                String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
                String originalPlaceholder = placeholder;
                if (visitedPlaceholders == null) {
                    visitedPlaceholders = new HashSet<>(4);
                }
                if (!visitedPlaceholders.add(originalPlaceholder)) {
                    throw new IllegalArgumentException(String.format("在属性 %s 的定义中包含循环的占位符引用！", originalPlaceholder));
                }
                // 递归调用，分析占位符键中包含的占位符。
                placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
                // 现在获取完全解析的键的值...
                String propVal = placeholderResolver.resolvePlaceholder(placeholder);
                if (propVal == null && this.valueSeparator != null) {
                    int separatorIndex = placeholder.indexOf(this.valueSeparator);
                    if (separatorIndex != -1) {
                        String actualPlaceholder = placeholder.substring(0, separatorIndex);
                        String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
                        propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
                        if (propVal == null) {
                            propVal = defaultValue;
                        }
                    }
                }
                if (propVal != null) {
                    // 递归调用，分析以前解析的占位符值中包含的占位符
                    propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
                    result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
                    if (log.isTraceEnabled()) {
                        log.trace("解析占位符 '{}", placeholder);
                    }
                    startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
                } else if (this.ignoreUnresolvablePlaceholders) {
                    // 继续处理未处理的值
                    startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
                } else {
                    throw new IllegalArgumentException(String.format("无法在属性 %s 中解析占位符 %s！", value, placeholder));
                }
                visitedPlaceholders.remove(originalPlaceholder);
            } else {
                startIndex = -1;
            }
        }
        return result.toString();
    }
    
    /**
     * 查询占位符结束索引
     */
    private int findPlaceholderEndIndex(@NotNull CharSequence buf, int startIndex) {
        int index = startIndex + this.placeholderPrefix.length();
        int withinNestedPlaceholder = 0;
        while (index < buf.length()) {
            if (StrUtil.substringMatch(buf, index, this.placeholderSuffix)) {
                if (withinNestedPlaceholder > 0) {
                    withinNestedPlaceholder--;
                    index = index + this.placeholderSuffix.length();
                } else {
                    return index;
                }
            } else if (StrUtil.substringMatch(buf, index, this.simplePrefix)) {
                withinNestedPlaceholder++;
                index = index + this.simplePrefix.length();
            } else {
                index++;
            }
        }
        return -1;
    }
    
    
    /**
     * 用于解析字符串中包含的占位符的替换值的策略界面
     */
    @FunctionalInterface
    public interface PlaceholderResolver {
        
        /**
         * 将提供的占位符名称解析为替换值
         *
         * @param placeholderName 要解析的占位符的名称
         * @return 替换值，如果不进行替换，则为 {@code null}
         */
        @Nullable
        String resolvePlaceholder(String placeholderName);
    }
    
}
